home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / x11 / rpg / crossfir.92 / crossfir / crossfire-0.92.5 / server / skills.c < prev    next >
C/C++ Source or Header  |  1996-07-24  |  35KB  |  1,069 lines

  1.  
  2. /* Initial coding: 6 Sep 1994, Nick Williams (njw@cs.city.ac.uk) */
  3.  
  4. /* Generalized code + added hiding and lockpicking skills, */
  5. /* March 3, 1995, brian thomas (thomas@nomad.astro.psu.edu) */
  6.  
  7. /* Added more skills, fixed bug in stealing code */
  8. /* April 21, 1995, brian thomas (thomas@nomad.astro.psu.edu) */
  9.  
  10. /* Added more skills, fixed bugs, see skills.h */
  11. /* May/June, 1995, brian thomas (thomas@nomad.astro.psu.edu) */
  12.  
  13. /* July 95 Code re-vamped. Now we add the experience objects, all
  14.  * player activities which gain experience will bbe through the use
  15.  * of skillls. Thus, I added hand_weapons, missile_weapons, and 
  16.  * remove_traps skills -b.t. 
  17.  */
  18.  
  19. /* Aug 95 - Added more skills (disarm traps, spellcasting, praying). 
  20.  * Also, hand_weapons is now "melee_weapons". b.t. 
  21.  */
  22.  
  23. /* Oct 95 - changed the praying skill to accomodate MULTIPLE_GODS
  24.  * hack - b.t.
  25.  */
  26.  
  27. /* Dec 95 - modified the literacy and inscription (writing) skills. b.t. 
  28.  */ 
  29.  
  30. /* Mar 96 - modified the stealing skill. Objects with type FLESH or
  31.  * w/o a type cannot be stolen by players. b.t.
  32.  */
  33.  
  34. #include <global.h>
  35. #include <object.h>
  36. #ifndef __CEXTRACT__
  37. #include <sproto.h>
  38. #endif
  39. #include <skills.h>
  40. #include <spells.h>
  41. #include <book.h>
  42.  
  43. /* 
  44.  * When stealing: dependent on the intelligence/wisdom of whom you're
  45.  * stealing from (op in attempt_steal), offset by your dexterity and
  46.  * skill at stealing. They may notice your attempt, whether successful
  47.  * or not. 
  48.  */
  49.  
  50. int
  51. attempt_steal(object* op, object* who)
  52. {
  53.     object* success = 0;    /* did we get anything? */
  54.     int alarmed = 0;        /* was the thief caught? */
  55.     int chance, lvl;
  56.     int stats_value = get_weighted_skill_stats(who);
  57.     int lbonus = who->type==PLAYER ? who->chosen_skill->level/2 : 10;
  58.     object* tmp;
  59.     object* next;
  60.  
  61.     if (op->type == PLAYER || QUERY_FLAG(op, FLAG_MONSTER)) {
  62.     /* Go thru their inventory, stealing */
  63.     for(tmp = op->inv; tmp != NULL; tmp = next) {
  64.         next = tmp->below;
  65.  
  66.         /* you can't steal worn items, starting items, wiz stuff, 
  67.          * innate abilities, flesh or items w/o a type. Generally 
  68.          * speaking, the invisibility flag prevents experience or 
  69.          * abilities from being stolen since these types are currently
  70.          * always invisible archs. I was implicit here so as to prevent 
  71.          * future possible problems. -b.t. */
  72.         if (QUERY_FLAG(tmp,FLAG_WAS_WIZ) || QUERY_FLAG(tmp, FLAG_APPLIED) 
  73.         || !(tmp->type) || tmp->type == FLESH
  74.         || tmp->type == EXPERIENCE || tmp->type == ABILITY
  75.         || QUERY_FLAG(tmp,FLAG_STARTEQUIP)
  76.         || tmp->invisible )
  77.             continue;
  78.  
  79.         /* Okay, try stealing this item. Dependent on dexterity of thief */
  80.         /* NYI: Also, dependent on value of thievery skill */
  81.         /* The current calculation gives a random number around 15 */
  82.  
  83.         /* Note -- its probably much harder to steal from hostile
  84.          * beings! - alter the chances of success to reflect this 
  85.          */
  86.  
  87.         if(QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE))
  88.         chance = (RANDOM()%30+RANDOM()%30)/2;
  89.         else
  90.         chance = (RANDOM()%60+RANDOM()%60)/2;
  91.  
  92.         /* Nov 95 - toss in a level adjustment too. Its harder to steal
  93.          * from higher level beings.  */
  94.         lvl = op->level>lbonus ? op->level/2 : 0;
  95.  
  96.         if (chance < (stats_value + lbonus - lvl)) {
  97.         pick_up(who, tmp);
  98.         /* If you steal something heavy off them, they're bound to notice */
  99.         if (tmp->weight > (200*(RANDOM()%(stats_value+lbonus))-(RANDOM()*5+1))) {
  100.             alarmed = 1;
  101.         }
  102.         if(can_pick(who,tmp)) success = tmp;
  103.         break;
  104.         }
  105.     }
  106.     }
  107.     if (alarmed || RANDOM()%25 > stats_value) {
  108.     /* play_sound("stop! thief!"); kindofthing */
  109.     if (op->type != PLAYER) {
  110.         /* The unaggressives look after themselves 8) */
  111.         CLEAR_FLAG(op, FLAG_UNAGGRESSIVE);
  112.         if(who->type==PLAYER) npc_call_help(op);
  113.     } else {
  114.         char buf[MAX_BUF];
  115.         /* Notify the other player */
  116.         if (success && who->stats.Int > RANDOM()%20) {
  117.         sprintf(buf, "Your %s has gone missing!", query_name(success));
  118.         } else {
  119.         sprintf(buf, "Your pack feels strangely lighter.");
  120.         }
  121.         new_draw_info(NDI_UNIQUE, 0,op,buf);
  122.         if (!success) {
  123.         if (who->invisible) {
  124.             sprintf(buf, "you feel itchy fingers getting at your pack.");
  125.         } else {
  126.             sprintf(buf, "%s looks very shifty.", query_name(who));
  127.         }
  128.         new_draw_info(NDI_UNIQUE, 0,op,buf);
  129.         }
  130.     }
  131.     }
  132.     return success? 1:0;
  133. }
  134.  
  135. int steal(object* op, int dir)
  136. {
  137.     object *tmp, *next;
  138.     int x = op->x + freearr_x[dir];
  139.     int y = op->y + freearr_y[dir];
  140.  
  141.     if(dir == 0) {
  142.     /* Can't steal from ourself! */
  143.     return 0;
  144.     }
  145.  
  146.     if(wall(op->map,x,y)) {
  147.     return 0;
  148.     }
  149.  
  150.     /* Find the topmost object at this spot */
  151.     for(tmp = get_map_ob(op->map,x,y);
  152.     tmp != NULL && tmp->above != NULL;
  153.         tmp = tmp->above);
  154.  
  155.     /* For all the stacked objects at this point, attempt a steal */
  156.     for(; tmp != NULL; tmp = next) {
  157.       next = tmp->below;
  158.       /* Minor hack--for multi square beings - make sure we get 
  159.        * the 'head' coz 'tail' objects have no inventory! - b.t. 
  160.        */ 
  161.       if (tmp->head) tmp=tmp->head;
  162.       if (attempt_steal(tmp, op)) {
  163.       /* no experience for stealing from another player */
  164.       if(tmp->type==PLAYER)
  165.          return 0;
  166.       else
  167.          return (calc_skill_exp(op,tmp));
  168.       }
  169.     }
  170.     return 0;
  171. }
  172.  
  173. /* Implementation by bt. (thomas@nomad.astro.psu.edu)
  174.  * monster implementation 7-7-95 by bt.
  175.  */
  176.  
  177. int pick_lock(object *pl, int dir)
  178. {
  179.     char buf[MAX_BUF];
  180.     object *tmp; 
  181.     int x = pl->x + freearr_x[dir];
  182.     int y = pl->y + freearr_y[dir];
  183.     int success = 0;
  184.  
  185.     if(!dir) dir=pl->facing;
  186.  
  187. /* For all the stacked objects at this point find a door*/
  188.  
  189.     sprintf(buf, "There is no lock there.");
  190.  
  191.     for(tmp=get_map_ob(pl->map,x,y); tmp; tmp=tmp->above) {
  192.       if(!tmp) continue;
  193.       switch(tmp->type) { 
  194.         case DOOR:
  195.             if (attempt_pick_lock(tmp, pl)) { 
  196.         success = 1;
  197.                 sprintf(buf, "you pick the lock.");
  198.             } else 
  199.                 sprintf(buf, "you fail to pick the lock.");
  200.         break;
  201.         case LOCKED_DOOR: 
  202.             sprintf(buf, "you can't pick that lock!");
  203.         break;
  204.        default: 
  205.         break;
  206.       }
  207.     }
  208.     new_draw_info(NDI_UNIQUE, 0,pl,buf);
  209.     if(success)
  210.         return calc_skill_exp(pl,NULL);
  211.     else 
  212.     return 0;
  213. }      
  214.  
  215. int attempt_pick_lock ( object *door, object *pl)
  216. {
  217.     int bonus = SK_level(pl);
  218.     int difficulty= pl->map->difficulty ? pl->map->difficulty : 0;
  219.     int dex = get_skill_stat1(pl) ? get_skill_stat1(pl) : 10;
  220.     int success = 0, number;        /* did we get anything? */
  221.  
  222.   /* If has can_pass set, then its not locked! */
  223.    if(!QUERY_FLAG(door,FLAG_NO_PASS)) return 0;
  224.  
  225.   /* Try to pick the lock on this item (doors only for now). 
  226.    * Dependent on dexterity/skill SK_level of the player and  
  227.    * the map level difficulty. 
  228.    */
  229.  
  230.     number = (RANDOM()%40+RANDOM()%40)/2; 
  231.     if (number < ((dex + bonus) - difficulty)) { 
  232.       remove_door(door);
  233.       success = 1;
  234.     } else if (door->inv && door->inv->type==RUNE) {  /* set off any traps? */ 
  235.         spring_trap(door->inv,pl);            
  236.     } 
  237.     return success;
  238. }
  239.  
  240. /* HIDE CODE. Right now, user becomes 'invisible' for
  241.  * a short while (success and duration dependant on player SK_level,
  242.  * dexterity, charisma, and map difficulty
  243.  * Players have a good chance of becoming 'unhidden' if they move
  244.  * and like invisiblity will be come visible if they attack
  245.  * Implemented by b.t. (thomas@nomad.astro.psu.edu)
  246.  * July 7, 1995 - made hiding possible for monsters. -b.t.
  247.  */ 
  248.  
  249. int hide(object *op) {
  250.   char buf[MAX_BUF];
  251.   int level= SK_level(op);
  252.  
  253. /* the preliminaries -- Can we really hide now? */
  254. /* this keeps monsters from using invisibilty spells and hiding */
  255.  
  256.   if (QUERY_FLAG(op, FLAG_MAKE_INVIS)) {
  257.         sprintf(buf,"You don't need to hide while invisible!");
  258.         new_draw_info(NDI_UNIQUE, 0,op,buf);
  259.         return 0;
  260.   } else if (!op->hide && op->invisible>0 && op->type == PLAYER) { 
  261.         sprintf(buf,"Your attempt to hide breaks the invisibility spell!"); 
  262.         op->invisible= 0;
  263.         op->contr->tmp_invis=0;
  264.     if(QUERY_FLAG(op, FLAG_UNDEAD)) CLEAR_FLAG(op, FLAG_UNDEAD);
  265.         new_draw_info(NDI_UNIQUE, 0,op,buf);
  266.     update_object(op);
  267.         return 0;
  268.   } 
  269.  
  270.   if(op->invisible>(50*level)) {
  271.        new_draw_info(NDI_UNIQUE,0,op,"You are as well hidden as you can get."); 
  272.        return 0;
  273.   }
  274.   
  275.   if(attempt_hide(op)) { 
  276.      new_draw_info(NDI_UNIQUE, 0,op,"You hide in the shadows.");
  277.      update_object(op);
  278.      return calc_skill_exp(op, NULL);
  279.   } 
  280.   new_draw_info(NDI_UNIQUE,0,op,"You fail to conceal yourself.");
  281.   return 0; 
  282. }
  283.  
  284. int attempt_hide(object *op) {
  285.   int level = SK_level(op)/5;
  286.   int difficulty=op->map->difficulty;
  287.   int number;
  288.   int dexterity = get_skill_stat1(op); 
  289.   int charisma = get_skill_stat2(op)/2; 
  290.   int success = 0;
  291.  
  292.   /* safety. perhaps its a monster that is hiding */
  293.   charisma = charisma ? charisma : 10;        
  294.   dexterity = dexterity ? dexterity : 15;
  295.  
  296. /*  Hiding success and duration dependant on SK_level,
  297.  *  dexterity, charisma, and map difficulty
  298.  */
  299.  
  300.     number = (RANDOM()%20+RANDOM()%30)/2;
  301.     if (number < (dexterity + level - difficulty - (charisma/2))) {
  302.     success = 1;
  303.     op->invisible += 100;  /* set the level of 'hiddeness' */
  304.     if(op->type==PLAYER)
  305.         op->contr->tmp_invis=1;
  306.     op->hide=1;
  307.     }
  308.     return success;
  309. }
  310.  
  311. /* jump() - this is both a new type of movement for player/monsters and
  312.  * an attack as well. -b.t.
  313.  */ 
  314.  
  315. int jump(object *pl, int dir) 
  316. {
  317.  char buf[MAX_BUF];
  318.  int spaces=0,stats;
  319.  int str = get_skill_stat1(pl); 
  320.  int dex = get_skill_stat2(pl);
  321.  
  322.  dex = dex ? dex : 15;
  323.  str = str ? str : 10; 
  324.  
  325.         stats=str*str*str*dex;
  326.  
  327.      if(pl->carrying!=0)        /* don't want div by zero !! */     
  328.          spaces=(int) (stats/pl->carrying);
  329.      else
  330.         spaces=2;    /* pl has no objects - gets the far jump */ 
  331.  
  332.      if(spaces>2)
  333.          spaces = 2;
  334.      else if(spaces==0) {
  335.         sprintf(buf, "You are carrying too much weight to jump.");
  336.           new_draw_info(NDI_UNIQUE, 0,pl,buf);
  337.         return 0;
  338.      }
  339.      return attempt_jump(pl,dir,spaces);
  340. }
  341.  
  342. int attempt_jump (object *pl, int dir, int spaces) {
  343.   char buf[MAX_BUF];
  344.   object *tmp;
  345.   int i,exp=0,dx=freearr_x[dir],dy=freearr_y[dir];
  346.  
  347.  /* Jump loop. Go through spaces opject wants to jump. Halt the
  348.   * jump if a wall or creature is in the way. We set FLAG_FLYING
  349.   * temporarily to allow player to aviod exits/archs that are not 
  350.   * fly_on, fly_off. This will also prevent pickup of objects 
  351.   * while jumping over them.  
  352.   */ 
  353.  
  354.   remove_ob(pl);
  355.   SET_FLAG(pl,FLAG_FLYING);
  356.   for(i=0;i<=spaces;i++) { 
  357.       for(tmp=get_map_ob(pl->map,pl->x+dx,pl->y+dy);
  358.         tmp;tmp=tmp->above) { 
  359.        if(wall(tmp->map,tmp->x,tmp->y)) {           /* Jump into wall*/ 
  360.          sprintf(buf, "Your jump is blocked.");
  361.              new_draw_info(NDI_UNIQUE, 0,pl,buf);
  362.          (void) stop_jump(pl,i,spaces);
  363.          return 0;
  364.        } 
  365.        if(QUERY_FLAG(tmp,FLAG_MONSTER)          /* Jump into creature */ 
  366.                 || tmp->type==PLAYER ) {   
  367.          sprintf(buf, "You jump into%s%s.", 
  368.            tmp->type == PLAYER ? " " : " the ", tmp->name);
  369.              new_draw_info(NDI_UNIQUE, 0,pl,buf);
  370. #ifdef SIMPLE_PARTY_SYSTEM
  371.          if(tmp->type!=PLAYER || pl->contr->party_number==-1 ||
  372.          pl->contr->party_number!=tmp->contr->party_number) 
  373. #endif
  374.                   exp = skill_attack(tmp,pl,pl->facing,"kicked"); /* pl makes an attack */ 
  375.          (void) stop_jump(pl,i,spaces);
  376.          return exp;  /* note that calc_skill_exp() is already called by skill_attack() */ 
  377.        }
  378.        if(tmp->type==EXIT                 /* pl jump through exit */ 
  379.         && QUERY_FLAG(tmp, FLAG_FLY_ON)) { 
  380.              pl->x+=dx,pl->y+=dy;
  381.          (void) stop_jump(pl,i,spaces);
  382.          return calc_skill_exp(pl,NULL);
  383.        }
  384.       }
  385.       if(out_of_map(pl->map,pl->x+dx,pl->y+dy)) {
  386.           (void) stop_jump(pl,i,spaces);
  387.         return calc_skill_exp(pl,NULL);
  388.       } else  
  389.         pl->x+=dx,pl->y+=dy;
  390.   }
  391.   (void) stop_jump(pl,i,spaces);
  392.   return calc_skill_exp(pl,NULL);
  393. }
  394.  
  395. /* stop_jump() - End of jump. Clear flags, restore the map, and 
  396.  * freeze the jumper a while to simulate the exhaustion
  397.  * of jumping.
  398.  */
  399.  
  400. int stop_jump(object *pl, int dist, int spaces) {
  401.  /* int load=dist/(pl->speed*spaces); */ 
  402.  
  403.   CLEAR_FLAG(pl,FLAG_FLYING);
  404.   insert_ob_in_map(pl,pl->map);
  405.   draw(pl);  
  406.  /* pl->speed_left= (int) -FABS((load*8)+1); */ 
  407.   return 0;
  408. }
  409.  
  410. /* skill_ident() - this code is supposed to allow players to identify 
  411.  * classes of objects with the various "auto-ident" skills. Player must 
  412.  * have unidentified objects of the right type in order for the skill
  413.  * to work. While multiple classes of objects may be identified, 
  414.  * this code is kind of yucky -- it would be nice to make it a bit
  415.  * more generalized. Right now, skill indices are embedded in this routine.  
  416.  * Returns amount of experience gained (on successful ident).
  417.  * - b.t. (thomas@astro.psu.edu) 
  418.  */
  419.  
  420. int skill_ident(object *pl) {
  421.   char buf[MAX_BUF];
  422.   int success=0;
  423.  
  424.     if(!pl->chosen_skill)     /* should'nt happen... */ 
  425.         return 0;
  426.  
  427.     if(pl->type != PLAYER) return 0;  /* only players will skill-identify */
  428.  
  429.         sprintf(buf, "You look at the objects you are carrying...");
  430.         new_draw_info(NDI_UNIQUE, 0,pl,buf);   
  431.  
  432.     switch (pl->chosen_skill->stats.sp) {
  433.        case SK_SMITH: 
  434.               success += do_skill_ident(pl,WEAPON) + do_skill_ident(pl,ARMOUR)
  435.             + do_skill_ident(pl,BRACERS) + do_skill_ident(pl,CLOAK)
  436.             + do_skill_ident(pl,BOOTS) + do_skill_ident(pl,SHIELD)
  437.             + do_skill_ident(pl,GIRDLE) + do_skill_ident(pl,HELMET) 
  438.             + do_skill_ident(pl,GLOVES);
  439.         break;
  440.        case SK_BOWYER:
  441.         success += do_skill_ident(pl,BOW) + do_skill_ident(pl,ARROW);
  442.                 break;
  443.        case SK_ALCHEMY:
  444.                 success += do_skill_ident(pl,POTION) + do_skill_ident(pl,POISON)
  445.             + do_skill_ident(pl,AMULET) + do_skill_ident(pl,CONTAINER)
  446.             + do_skill_ident(pl,DRINK) + do_skill_ident(pl,INORGANIC);
  447.         break;
  448.            case SK_WOODSMAN:  
  449.                 success += do_skill_ident(pl,FOOD) + do_skill_ident(pl,DRINK) 
  450.             + do_skill_ident(pl,FLESH);
  451.                 break; 
  452.            case SK_JEWELER:
  453.                 success += do_skill_ident(pl,GEM) + do_skill_ident(pl,RING);
  454.                 break; 
  455.        case SK_LITERACY:
  456.                 success += do_skill_ident(pl,SPELLBOOK) 
  457.             + do_skill_ident(pl,SCROLL) + do_skill_ident(pl,BOOK);
  458.         break;
  459.            case SK_THAUMATURGY:
  460.                 success += do_skill_ident(pl,WAND) + do_skill_ident(pl,ROD) 
  461.             + do_skill_ident(pl,HORN);
  462.                 break;
  463.        case SK_DET_CURSE:
  464.                 success = do_skill_detect_curse(pl);
  465.                 if(success) 
  466.                    new_draw_info(NDI_UNIQUE, 0,pl,"...and discover cursed items!");
  467.                 break;   
  468.        case SK_DET_MAGIC:
  469.         success = do_skill_detect_magic(pl);
  470.         if(success)
  471.                       new_draw_info(NDI_UNIQUE, 0,pl,
  472.               "...and discover items imbued with mystic forces!");
  473.         break;
  474.        default:
  475.         LOG(llevError,"Error: bad call to skill_ident()");
  476.         return 0;
  477.         break;
  478.     } 
  479.         if(success && pl->type == PLAYER)
  480.         draw_inventory(pl);
  481.     else if(!success) {
  482.         sprintf(buf,"...and learn nothing more.");
  483.             new_draw_info(NDI_UNIQUE, 0,pl,buf);
  484.     }
  485.  
  486.         return success;
  487. }
  488.  
  489. int do_skill_detect_curse(object *pl) {
  490. object *tmp;
  491. int success=0;
  492.  
  493. /* check the player inventory - stop after 1st success or 
  494.  * run out of unidented items 
  495.  */ 
  496.     for(tmp=pl->inv;tmp;tmp=tmp->below)
  497.         if(!QUERY_FLAG(tmp,FLAG_IDENTIFIED) && !QUERY_FLAG(tmp,FLAG_KNOWN_CURSED)
  498.             && (QUERY_FLAG(tmp,FLAG_CURSED) || QUERY_FLAG(tmp,FLAG_DAMNED)) ) {
  499.                 SET_FLAG(tmp,FLAG_KNOWN_CURSED);
  500.                 success+=calc_skill_exp(pl,tmp);
  501.         }
  502.     return success;
  503. }
  504.  
  505. int do_skill_detect_magic(object *pl) {
  506. object *tmp;
  507. int success=0;
  508.  
  509. /* check the player inventory - stop after 1st success or
  510.  * run out of unidented items
  511.  */
  512.     for(tmp=pl->inv;tmp;tmp=tmp->below)
  513.         if(!QUERY_FLAG(tmp,FLAG_IDENTIFIED) && !QUERY_FLAG(tmp,FLAG_KNOWN_MAGICAL)
  514.         && (is_magical(tmp) || always_magical(tmp)) ) { 
  515.                 SET_FLAG(tmp,FLAG_KNOWN_MAGICAL);
  516.         success+=calc_skill_exp(pl,tmp);
  517.     }
  518.     return success;
  519. }
  520.  
  521. /* do_skill_ident() - workhorse for skill_ident() -b.t. */
  522. /*  Sept 95. I put in a probability for identification of artifacts.
  523.  *  highly magical artifacts will be more difficult to ident -b.t.
  524.  */
  525. int do_skill_ident(object *pl, int obj_class) {
  526.   object *tmp;
  527.   int success=0,chance;
  528.   int skill_value = SK_level(pl) + get_weighted_skill_stats(pl);
  529.  
  530. /* check the player inventory */
  531.     for(tmp=pl->inv;tmp;tmp=tmp->below)
  532.     if(!QUERY_FLAG(tmp,FLAG_IDENTIFIED) && !QUERY_FLAG(tmp,FLAG_NO_SKILL_IDENT) 
  533.        && need_identify(tmp) 
  534.        && !tmp->invisible && tmp->type==obj_class) { 
  535.         chance = RANDOM()%10+RANDOM()%10+RANDOM()%10+
  536.             RANDOM()%(tmp->magic ? tmp->magic*5 : 1); 
  537.         if(skill_value >= chance) {
  538.           identify(tmp);
  539.                if (pl->type==PLAYER) {
  540.                 new_draw_info_format(NDI_UNIQUE, 0, pl,
  541.                       "You have %s.", long_desc(tmp));
  542.                 if (tmp->msg) {
  543.                   new_draw_info(NDI_UNIQUE, 0,pl, "The item has a story:");
  544.                   new_draw_info(NDI_UNIQUE, 0,pl, tmp->msg);
  545.                 }
  546.                 if (pl->contr->eric_server > 0)
  547.                     esrv_send_item(pl, tmp);
  548.               }   
  549.               success += calc_skill_exp(pl,tmp);
  550.             } else 
  551.           SET_FLAG(tmp, FLAG_NO_SKILL_IDENT);
  552.         }
  553.     return success;
  554. }  
  555.  
  556. /* players using this skill can 'charm' a monster --
  557.  * into working for them. It can only be used on 
  558.  * non-special (see below) 'neutral' creatures. 
  559.  * -b.t. (thomas@nomad.astro.psu.edu)
  560.  */
  561.  
  562. int use_oratory(object *pl, int dir) {
  563.   int expsum=0,x=pl->x+freearr_x[dir],y=pl->y+freearr_y[dir];
  564.   int stat1 = get_skill_stat1(pl);
  565.   object *tmp;
  566.  
  567.     if(pl->type!=PLAYER) return 0;    /* only players use this skill */ 
  568.  
  569.         for(tmp=get_map_ob(pl->map,x,y);tmp;tmp=tmp->above) { 
  570.             if(!tmp) return 0;
  571.         if(!QUERY_FLAG(tmp,FLAG_MONSTER)) continue; 
  572.             if(tmp->type==PLAYER) continue;          /* can't persude players! */ 
  573.  
  574.                 new_draw_info_format(NDI_UNIQUE, 
  575.             0,pl, "You orate to the %s.",query_name(tmp));
  576.  
  577.     /* the following conditions limit who may be 'charmed' */
  578.  
  579.         if(!QUERY_FLAG(tmp,FLAG_UNAGGRESSIVE) &&   /* it's hostile! */ 
  580.             !QUERY_FLAG(tmp, FLAG_FRIENDLY)) {  
  581.                         new_draw_info_format(NDI_UNIQUE, 0,pl, 
  582.                "Too bad the %s is'nt listening!\n",query_name(tmp));
  583.             return 0;    
  584.         }
  585.            /* it's already allied! */ 
  586.                 if(QUERY_FLAG(tmp,FLAG_FRIENDLY)&&(tmp->move_type==PETMOVE)){  
  587.             if(get_owner(tmp)==pl) {
  588.                            new_draw_info(NDI_UNIQUE, 0,pl, 
  589.                              "Your follower loves your speach.\n");
  590.                return 0;
  591.              } else if(SK_level(pl)>tmp->level) { /* you steal the follower! */
  592.                   set_owner(tmp,pl);    
  593.                            new_draw_info_format(NDI_UNIQUE, 0,pl, 
  594.                              "You convince the %s to follow you instead!\n"
  595.                  ,query_name(tmp));
  596.                return calc_skill_exp(pl,tmp);
  597.             }
  598.                 } 
  599.  
  600.             if(tmp->more || tmp->head) continue;  /* no multiple square monsters*/
  601.         if(tmp->msg) continue;                 /* no special message monsters */
  602.  
  603.     /* Ok, got a 'sucker' lets try to make them a follower */
  604.             if((tmp->level+1)<(SK_level(pl)+RANDOM()%(stat1))) { 
  605.                         new_draw_info_format(NDI_UNIQUE, 0,pl, 
  606.               "You convince the %s to become your follower.\n"
  607.               ,query_name(tmp));
  608.                    set_owner(tmp,pl);
  609.                 SET_FLAG(tmp,FLAG_MONSTER);
  610.                 tmp->stats.exp = 0;
  611.                       add_friendly_object(tmp);
  612.                 SET_FLAG(tmp,FLAG_FRIENDLY);
  613.                 tmp->move_type = PETMOVE;
  614.              expsum += calc_skill_exp(pl,tmp);
  615.         }
  616.     /* maybe we made it angry */ 
  617.             if((SK_level(pl)+((stat1-10)/2))<RANDOM()%((2*tmp->level)+1)) { 
  618.                         new_draw_info_format(NDI_UNIQUE, 0,pl, 
  619.               "Your speach angers the %s!\n",query_name(tmp)); 
  620.                 if(QUERY_FLAG(tmp,FLAG_FRIENDLY)) {
  621.                     CLEAR_FLAG(tmp,FLAG_FRIENDLY);
  622.                     tmp->move_type = 0;     /* needed? */ 
  623.             }
  624.                 CLEAR_FLAG(tmp,FLAG_UNAGGRESSIVE);
  625.         }
  626.     }
  627.     return expsum;
  628. }
  629.  
  630. /* Singing() -this skill allows the player to pacify nearby creatures.
  631.  * There are few limitations on who/what kind of 
  632.  * non-player creatures that may be pacified. Right now, a player
  633.  * may pacify creatures which have Int == 0. In this routine, once
  634.  * successfully pacified the creature gets Int=1. Thus, a player 
  635.  * may only pacify a creature once. 
  636.  * BTW, I appologize for the naming of the skill, I couldnt think
  637.  * of anything better! -b.t. 
  638.  */
  639.  
  640. int singing(object *pl, int dir) {
  641.   int i,exp = 0,stat1=get_skill_stat1(pl);
  642.   object *tmp;
  643.  
  644.         if(pl->type!=PLAYER) return 0;    /* only players use this skill */
  645.  
  646.         new_draw_info_format(NDI_UNIQUE,0,pl, "You sing");
  647.         for(i=dir;i<(dir+MIN(SK_level(pl),SIZEOFFREE));i++) 
  648.             for(tmp=get_map_ob(pl->map,pl->x+freearr_x[i],pl->y+freearr_y[i]);
  649.           tmp;tmp=tmp->above) {
  650.                 if(!tmp) return 0;
  651.         if(!QUERY_FLAG(tmp,FLAG_MONSTER)) continue;
  652.          /* can't affect players */
  653.                 if(tmp->type==PLAYER || tmp->stats.Int) continue;   
  654.  
  655.  
  656.         /* the following monsters can't be calmed */
  657.  
  658.         if(QUERY_FLAG(tmp,FLAG_SPLITTING)    /* have no ears! */ 
  659.            || QUERY_FLAG(tmp,FLAG_HITBACK)) break;    
  660.  
  661.         if(tmp->stats.Int>0) break;    /* is too smart */
  662.         if(tmp->level>SK_level(pl)) break;    /* too powerfull */
  663.         if(QUERY_FLAG(tmp,FLAG_UNDEAD)) /* undead dont listen! */ 
  664.                 break;
  665.  
  666.         if(QUERY_FLAG(tmp,FLAG_UNAGGRESSIVE) /* already calm */ 
  667.           ||QUERY_FLAG(tmp,FLAG_FRIENDLY)) 
  668.             break;    
  669.  
  670.         if((tmp->level)<((RANDOM()%((SK_level(pl)+1)*2))+(stat1-9)/2)){
  671.              SET_FLAG(tmp,FLAG_UNAGGRESSIVE);
  672.                      new_draw_info_format(NDI_UNIQUE, 0,pl,
  673.                        "You calm down the %s\n",query_name(tmp));
  674.              tmp->stats.Int = 1; /* this prevents re-pacification */
  675.              exp += calc_skill_exp(pl,tmp);
  676.         } else { 
  677.                      new_draw_info_format(NDI_UNIQUE, 0,pl,
  678.                       "Too bad the %s is'nt listening!\n",query_name(tmp));
  679.         }
  680.         }
  681.     return exp;
  682. }
  683.  
  684. /* The FIND_TRAPS skill. This routine is taken mostly from the 
  685.  * command_search loop. It seemed easier to have a separate command,
  686.  * rather than overhaul the existing code - this makes sure things 
  687.  * still work for those people who don't want to have skill code 
  688.  * implemented.
  689.  */
  690.  
  691. int find_traps (object *pl) {  
  692.    object *tmp,*tmp2;
  693.    int i,expsum=0;
  694.   /*First we search all around us for runes and traps, which are
  695.     all type RUNE */
  696.    for(i=0;i<9;i++) { 
  697.         /*  Check everything in the square for trapness */
  698.         if(out_of_map(pl->map,pl->x + freearr_x[i],pl->y + freearr_y[i])) continue;
  699.         for(tmp = get_map_ob(pl->map, pl->x + freearr_x[i], pl->y +freearr_y[i]);
  700.             tmp!=NULL;tmp=tmp->above) {
  701.  
  702.             /*  And now we'd better do an inventory traversal of each
  703.                 of these objects' inventory */
  704.  
  705.             for(tmp2=tmp->inv;tmp2!=NULL;tmp2=tmp2->below)
  706.                 if(tmp2->type==RUNE)  
  707.           if(trap_see(pl,tmp2)) { 
  708.             trap_show(tmp2,tmp); 
  709.               if(tmp2->stats.Cha>1) { 
  710.                 expsum += calc_skill_exp(pl,tmp2);
  711.                 /* do the following so calc_skill_exp will know 
  712.                      * how much xp to award for disarming*/
  713.                 tmp2->stats.exp = tmp2->stats.Cha * tmp2->level; 
  714.                 tmp2->stats.Cha = 1; /* unhide the trap */ 
  715.             }
  716.           }
  717.             if(tmp->type==RUNE)  
  718.           if(trap_see(pl,tmp)) { 
  719.             trap_show(tmp,tmp); 
  720.               if(tmp->stats.Cha>1) {
  721.                 expsum += calc_skill_exp(pl,tmp);
  722.                 tmp->stats.exp = tmp->stats.Cha * tmp->level; 
  723.                 tmp->stats.Cha = 1; /* unhide the trap */ 
  724.             }
  725.                   }
  726.        }
  727.    }
  728.    return expsum;
  729. }  
  730.  
  731. /* pray() - when this skill is called from do_skill(), it allows
  732.  * the player to regain lost grace points at a faster rate. -b.t.
  733.  */
  734.  
  735. /* Oct 95 - altered it to allow for MULTIPLE_GODS hack */
  736.  
  737. int pray (object *pl) {
  738.   char buf[MAX_BUF];
  739.  
  740.   sprintf(buf,"You pray.");
  741.  
  742.   if(pl->type!=PLAYER) return 0;
  743. #ifdef MULTIPLE_GODS
  744.   else { /* look at the top object underneath the player, is it an altar? */ 
  745.     object *tmp=pl->below;
  746.  
  747.     if(tmp && !strcmp(tmp->arch->name,"altar")) 
  748.         if(tmp && tmp->title && pl->chosen_skill->exp_obj) { /* its owned by a God! */ 
  749.       char *pl_god = determine_god(pl);
  750.  
  751.           sprintf(buf,"You pray at the altar.");        
  752.  
  753.       /* hmm. what happend depends on pl previous god, level, etc */
  754.       if(!strcmp(pl_god,"none")||lookup_god_by_name(pl_god)==-1) {     /*new convert */
  755.         become_follower(pl,tmp->title);
  756.         return 0;
  757.       } else if(!strcmp(pl_god,tmp->title)) { /* pray at your gods altar */  
  758.         int bonus = ((pl->stats.Wis/10)+(SK_level(pl)/10));
  759.          /* we can get neg grace up faster */
  760.         if(pl->stats.grace<0) pl->stats.grace+=(bonus>-1*(pl->stats.grace/10) ?
  761.             bonus : -1*(pl->stats.grace/10)); 
  762.          /* we can super-charge grace to 2x max */
  763.         if(pl->stats.grace<(2*pl->stats.maxgrace)) {
  764.         pl->stats.grace+=bonus/2;
  765.            sprintf(buf,"%s is pleased with your worship.",pl_god); 
  766.         }
  767.         /* Every once in a while, the god decides to checkup on their
  768.          * follower, and may intervene to help them out. */
  769.         if((RANDOM()%500-bonus)<0) 
  770.             god_intervention(pl,pl_god);
  771.  
  772.       } else if(tmp->title) { /* praying to another god! */ 
  773.          int loss = 0;
  774.  
  775.             new_draw_info_format(NDI_UNIQUE|NDI_NAVY,0,pl,
  776.                 "Heretic! %s is angered!",pl_god); 
  777.  
  778.               /* whether we will be successfull in defecting or not - 
  779.            * we lose experience from the clerical experience obj */
  780.             loss = -0.1 * (float) pl->chosen_skill->exp_obj->stats.exp;
  781.             loss = check_dm_add_exp_to_obj(pl->chosen_skill->exp_obj,loss);
  782.         pl->chosen_skill->exp_obj->stats.exp += loss;
  783.             pl->stats.exp += loss;
  784.             add_exp(pl,0);
  785.             draw_stats(pl);
  786.  
  787.           /* May switch Gods, but its random chance based on our current level 
  788.            * note it gets harder to swap gods the higher we get */
  789.         if(!(RANDOM()%pl->chosen_skill->exp_obj->level))   
  790.                become_follower(pl,tmp->title);
  791.         return 0;
  792.       } 
  793.         } 
  794.   }
  795. #endif
  796.  
  797.   new_draw_info(NDI_UNIQUE,0,pl,buf);
  798.  
  799.   if(pl->stats.grace < pl->stats.maxgrace) {
  800.            pl->stats.grace++;
  801.            pl->last_grace = -1;
  802.   } else return 0; 
  803.  
  804.   do_some_living(pl);
  805.  
  806.   return 0;
  807. }
  808.  
  809. /* This skill allows the player to regain a few sp or hp for a 
  810.  * brief period of concentration. No armour or weapons may be 
  811.  * wielded/applied for this to work. The amount of time needed
  812.  * to concentrate and the # of points regained is dependant on
  813.  * the level of the user. - b.t. thomas@astro.psu.edu 
  814.  */ 
  815.  
  816. /* July 95 I commented out 'factor' - this should now be handled by 
  817.  * get_skill_time() -b.t. */
  818.  
  819. /* Sept 95. Now meditation is level dependant (score). User may
  820.  * meditate w/ more armour on as they get higher level 
  821.  * Probably a better way to do this is based on overall encumberance 
  822.  * -b.t.
  823.  */ 
  824.  
  825. void meditate (object *pl) {
  826.   object *tmp;
  827.   int lvl = pl->level; 
  828.   /* int factor = 10/(1+(pl->level/10)+(pl->stats.Int/15)+(pl->stats.Wis/15)); */ 
  829.  
  830.     if(pl->type!=PLAYER) return;    /* players only */
  831.  
  832.     /* check if pl has removed encumbering armour and weapons */ 
  833.  
  834.     if(QUERY_FLAG(pl,FLAG_READY_WEAPON) && (lvl<6)) { 
  835.         new_draw_info(NDI_UNIQUE,0,pl, 
  836.       "You can't concentrate while wielding a weapon!\n");    
  837.     return;
  838.     } else {
  839.     for(tmp=pl->inv;tmp;tmp=tmp->below)
  840.           if(( (tmp->type==ARMOUR && lvl<12) 
  841.         || (tmp->type==HELMET && lvl<10) 
  842.         || (tmp->type==SHIELD && lvl<6) 
  843.         || (tmp->type==BOOTS && lvl<4) 
  844.         || (tmp->type==GLOVES && lvl<2) )
  845.            && QUERY_FLAG(tmp,FLAG_APPLIED)) {
  846.              new_draw_info(NDI_UNIQUE,0,pl, 
  847.           "You can't concentrate while wearing so much armour!\n");    
  848.           return;
  849.       } 
  850.     } 
  851.  
  852.     /* ok let's meditate!  Spell points are regained first, then once
  853.      * they are maxed we get back hp. Actual incrementing of values
  854.      * is handled by the do_some_living() (in player.c). This way magical
  855.      * bonuses for healing/sp regeneration are included properly
  856.      * No matter what, we will eat up some playing time trying to 
  857.      * meditate. (see 'factor' variable for what sets the amount of time) 
  858.      */
  859.  
  860.         new_draw_info(NDI_UNIQUE,0,pl, "You meditate."); 
  861.      /*   pl->speed_left -= (int) FABS(factor); */ 
  862.  
  863.     if(pl->stats.sp < pl->stats.maxsp) {
  864.        pl->stats.sp++;
  865.        pl->last_sp = -1;
  866.     } else if (pl->stats.hp < pl->stats.maxhp)  {
  867.        pl->stats.hp++;
  868.        pl->last_heal = -1;
  869.     } else return;
  870.     
  871.     do_some_living(pl);     
  872. }
  873.  
  874. /* write_on_item() - wrapper for write_note and write_scroll */
  875.  
  876. int write_on_item (object *pl,char *params) {
  877.   object *item=pl->inv;
  878.  
  879.         if(pl->type!=PLAYER) return 0;
  880.  
  881.     /* Need to be able to read before we can write! */
  882.  
  883.     if(!find_skill(pl,SK_LITERACY)) {
  884.         new_draw_info(NDI_UNIQUE,0,pl,
  885.            "You must learn to read before you can write!");
  886.         return 0;
  887.     }
  888.  
  889.         /* Check if we are ready to attempt inscription */
  890.     if(item) {
  891.             if(QUERY_FLAG(item,FLAG_UNPAID)) {
  892.                 new_draw_info(NDI_UNIQUE,0,pl,
  893.                         "You had better pay for that before you write on it.");
  894.                 return 0;
  895.             }
  896.         switch(item->type) {
  897.          case SCROLL:
  898.           return write_scroll(pl);
  899.           break;
  900.         case BOOK: { 
  901.               char *string=params;
  902.           int i;
  903.           /* if skill name occurs at begining of the string 
  904.            * we have to reset pointer to miss it */
  905.             if(lookup_skill_by_name(params)>=0) 
  906.             for(i=(strcspn(string," ")+1);i>0;i--) string++;
  907.           return write_note(pl,item,string);
  908.           break;
  909.         }
  910.         default:
  911.           break;
  912.         }
  913.         } 
  914.            new_draw_info_format(NDI_UNIQUE,0,pl,"You cannot write on %s", 
  915.              !item?"nothing":query_short_name(item));
  916.         return 0;
  917. }
  918.  
  919. /* write_note() - this routine allows players to inscribe messages in 
  920.  * ordinary 'books' (anything that is type BOOK). b.t.
  921.  */
  922.  
  923. int write_note(object *pl, object *item, char *msg) {
  924.   char buf[BOOK_BUF]; 
  925.   if(!item||item->type!=BOOK) return 0;
  926.   if(!msg) { 
  927.     new_draw_info(NDI_UNIQUE,0,pl,"No message to write!");
  928.     new_draw_info(NDI_UNIQUE,0,pl,"Usage: use_skill inscription <message>");
  929.     return 0;
  930.   }
  931.   if(!book_overflow(item->msg,msg,BOOK_BUF)) { /* add msg string to book */ 
  932.     if(item->msg) { 
  933.       strcpy(buf,item->msg);
  934.       strcat(buf,"\n"); /* new msg goes on a new line */ 
  935.       free_string(item->msg);
  936.     } 
  937.     strcat(buf,msg);
  938.     item->msg=add_string(buf); 
  939.     new_draw_info_format(NDI_UNIQUE,0,pl,
  940.        "You write in the %s.",query_short_name(item));
  941.     return strlen(msg);
  942.   } else
  943.     new_draw_info_format(NDI_UNIQUE,0,pl,
  944.         "Your message won't fit in the %s!",query_short_name(item)); 
  945.   return 0;
  946. }
  947.  
  948. /* write_scroll() - this routine allows players to inscribe spell scrolls
  949.  * of spells which they know. Backfire effects are possible with the
  950.  * severity of the backlash correlated with the difficulty of the scroll 
  951.  * that is attempted. -b.t. thomas@astro.psu.edu
  952.  */
  953.  
  954. int write_scroll (object *pl) {
  955.   object *scroll=pl->inv;
  956.   int success=0,confused=0,chosen_spell=-1,stat1=get_skill_stat1(pl);
  957.  
  958.     /* Check if we are ready to attempt inscription */
  959.     chosen_spell=pl->contr->chosen_spell;
  960.     if(chosen_spell<0) {
  961.              new_draw_info(NDI_UNIQUE,0,pl,
  962.             "You need a spell readied in order to inscribe!"); 
  963.         return 0; 
  964.     }
  965.     if(spells[chosen_spell].scroll_chance==0) { /* Tried to write non-scroll spell */
  966.              new_draw_info_format(NDI_UNIQUE,0,pl,"You can't inscribe the spell %s.",
  967.             spells[chosen_spell].name);
  968.         return 0;
  969.     }
  970.     if(spells[chosen_spell].sp>pl->stats.sp) {
  971.              new_draw_info_format(NDI_UNIQUE,0,pl,
  972.              "You don't have enough spell points to write a scroll of %s."
  973.               ,spells[chosen_spell].name);
  974.         return 0;
  975.     }
  976.  
  977.     /* ok, we are ready to try inscription */
  978.  
  979.     if(QUERY_FLAG(pl,FLAG_CONFUSED)) confused = 1;
  980.     success = RANDOM()%(spells[chosen_spell].level*4) < SK_level(pl) ? 1 : 0;
  981.     pl->stats.sp-=spells[chosen_spell].sp; /* lose sp no matter what */ 
  982.  
  983.     if(success) { 
  984.         if(scroll->nrof) scroll->nrof = 1;
  985.         if(!confused) {
  986.             scroll->level= (SK_level(pl)>spells[chosen_spell].level ? 
  987.                 SK_level(pl) : spells[chosen_spell].level);
  988.         } else {            /* a  confused scribe gets a random spell */ 
  989.             chosen_spell=0;
  990.             while (!spells[chosen_spell].scroll_chance) 
  991.                 chosen_spell=RANDOM()%NROFREALSPELLS; 
  992.             scroll->level=SK_level(pl)>spells[chosen_spell].level ? 
  993.                 spells[chosen_spell].level : ((RANDOM()%SK_level(pl))+1);
  994.         }
  995.  
  996.         if(scroll->stats.sp==chosen_spell) 
  997.                  new_draw_info(NDI_UNIQUE,0,pl,
  998.                 "You overwrite the scroll.");
  999.         else { 
  1000.                  new_draw_info(NDI_UNIQUE,0,pl,
  1001.                 "You succeed in writing a new scroll.");
  1002.             scroll->stats.sp=chosen_spell; 
  1003.         }
  1004.  
  1005.         draw_inventory(pl);
  1006.         success = calc_skill_exp(pl,scroll);
  1007.         if(!confused) success *= 2;
  1008.         return success; 
  1009.     } else  
  1010.         if(spells[chosen_spell].level>SK_level(pl) || confused){ /*backfire!*/
  1011.                 new_draw_info(NDI_UNIQUE,0,pl,
  1012.             "Ouch! Your attempt to write a new scroll strains your mind!");
  1013.            if(RANDOM()%2==1)   
  1014.             drain_specific_stat(pl,4); 
  1015.              else { 
  1016.                 confuse_player(pl,pl,99);
  1017.             return (-3*calc_skill_exp(pl,scroll));
  1018.            } 
  1019.         } else if(RANDOM()%stat1<15) { 
  1020.                 new_draw_info(NDI_UNIQUE,0,pl,
  1021.             "Your attempt to write a new scroll rattles your mind!");
  1022.            confuse_player(pl,pl,99);
  1023.         } else
  1024.                 new_draw_info(NDI_UNIQUE,0,pl,"You fail to write a new scroll.");
  1025.  
  1026.     return (-1*calc_skill_exp(pl,scroll));
  1027. }
  1028.  
  1029. /* remove_trap() - This skill will disarm any previously discovered trap 
  1030.  * the algorithm is based (almost totally) on the old command_disarm() - b.t. 
  1031.  */ 
  1032.  
  1033. int remove_trap (object *op, int dir) {
  1034.   object *tmp,*tmp2;
  1035.   int i,x,y,success=0;    
  1036.  
  1037.    for(i=0;i<9;i++) {
  1038.       x = op->x + freearr_x[i]; 
  1039.       y = op->y + freearr_y[i]; 
  1040.       if(out_of_map(op->map,x,y)) {
  1041.     new_draw_info(NDI_UNIQUE,0,op,"There are no traps there!");
  1042.     return 0;
  1043.       }
  1044.  
  1045.   /*  Check everything in the square for trapness */
  1046.    for(tmp = get_map_ob(op->map,x,y);tmp!=NULL;tmp=tmp->above) { 
  1047.  
  1048.       /* And now we'd better do an inventory traversal of each
  1049.        * of these objects' inventory */
  1050.  
  1051.       for(tmp2=tmp->inv;tmp2!=NULL;tmp2=tmp2->below) 
  1052.          if(tmp2->type==RUNE&&tmp2->stats.Cha<=1) {
  1053.               trap_show(tmp2,tmp);
  1054.               if(trap_disarm(op,tmp2,1))
  1055.            success += calc_skill_exp(op,tmp2);
  1056.          }
  1057.  
  1058.       if(tmp->type==RUNE&&tmp->stats.Cha<=1) {
  1059.          trap_show(tmp,tmp);
  1060.          if (trap_disarm(op,tmp,1))
  1061.            success += calc_skill_exp(op,tmp);
  1062.       }  
  1063.     }
  1064.   }
  1065.  
  1066.    return success;
  1067. }
  1068.  
  1069.